package com.feemanage.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.common.core.page.PageDomain;
import com.common.utils.sql.SqlUtil;
import com.feemanage.common.SearchUtils;
import com.feemanage.domain.ProjectType;
import com.feemanage.domain.bo.ProjectTypeExecuteBo;
import com.feemanage.domain.vo.ProjectBudgetExecuteVo;
import com.feemanage.domain.vo.ProjectExecuteVo;
import com.feemanage.domain.vo.ProjectTypeExecuteVo;
import com.feemanage.domain.vo.ProjectUnitExecuteVo;
import com.feemanage.mapper.ProjectTypeExecuteMapper;
import com.feemanage.service.IProjectTypeExecuteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ProjectTypeExecuteServiceImpl implements IProjectTypeExecuteService {

    @Autowired
    private ProjectTypeExecuteMapper baseMapper;

    /**
     * 查询列表
     * @param executeBo
     * @return
     */
    @Override
    public List<ProjectTypeExecuteVo> selectList(PageDomain pageDomain,ProjectTypeExecuteBo executeBo,String type) {
        QueryWrapper<ProjectTypeExecuteBo> wrapper = Wrappers.query();
        Map<String, Object> params = executeBo.getParams();
        wrapper.between(ObjectUtils.isNotEmpty(params.get("beginTime")) && ObjectUtils.isNotEmpty(params.get("endTime")),
                        "AUTHORIZE_DATE", params.get("beginTime"), params.get("endTime"))
                .ge(ObjectUtils.isNotEmpty(params.get("beginTime"))  && ObjectUtils.isEmpty(params.get("endTime")),"AUTHORIZE_DATE", params.get("beginTime"))
                .le(ObjectUtils.isEmpty(params.get("beginTime")) && ObjectUtils.isNotEmpty(params.get("endTime")),"AUTHORIZE_DATE", params.get("endTime"));
        if(StringUtils.isNotEmpty(executeBo.getProjectTypeCode())){
            List<String> list = Arrays.stream(executeBo.getProjectTypeCode().split(",")).collect(Collectors.toList());
            wrapper.in("project_type_code",list);
        }
        if(StringUtils.isNotEmpty(executeBo.getExecuteYear())){
            List<String> list = Arrays.stream(executeBo.getExecuteYear().split(",")).collect(Collectors.toList());
            wrapper.in("execute_year",list);
        }
        String orderByColumn = SqlUtil.escapeOrderBySql(pageDomain.getOrderByColumn());
        boolean isASC = SqlUtil.escapeOrderBySql(pageDomain.getIsAsc()).equalsIgnoreCase("asc");
        if (!StringUtils.isEmpty(orderByColumn))
        {
            wrapper.orderBy(true,isASC,orderByColumn);
        }
        List<ProjectTypeExecuteVo> list =  baseMapper.selectPageList(wrapper);
        if(type == null){
            List<ProjectType> typeList = baseMapper.selectProjectTypeList(new QueryWrapper<>());
            list =  buildTree(null,null,null,aggregate(list,typeList));
            Comparator<ProjectTypeExecuteVo> comparator = Comparator.comparing(o -> (String) ProjectTypeExecuteVo.getField(o,orderByColumn),Comparator.nullsFirst(Comparator.naturalOrder()));
            if(!isASC){
                list = list.stream().sorted(comparator.reversed()).collect(Collectors.toList());
            }else{
                list = list.stream().sorted(comparator).collect(Collectors.toList());
            }
        }
       return list;
    }

    public List<ProjectTypeExecuteVo> aggregate(List<ProjectTypeExecuteVo> list1, List<ProjectType> list2) {
        // 构建 code 到 parent 的映射
        Map<String, String> codeToParent = new HashMap<>();
        Map<String, String> codeToName = new HashMap<>();
        for (ProjectType codeInfo : list2) {
            codeToParent.put(codeInfo.getCode(), codeInfo.getUpCode());
            codeToName.put(codeInfo.getCode(),codeInfo.getName());
        }

        // 用于存储聚合结果的 Map
        Map<String, ProjectTypeExecuteVo> aggregatedMap = new HashMap<>();

        // 遍历 list1 中的每个项目
        for (ProjectTypeExecuteVo project : list1) {
            String currentCode = project.getProjectTypeCode();
            String authorizeDate = project.getAuthorizeDate();
            String executeYear = project.getExecuteYear();

            // 初始化当前项目的值
            BigDecimal incomeFee = project.getIncomeFee() != null ? project.getIncomeFee() : BigDecimal.ZERO;
            BigDecimal feeAuthorize = project.getFeeAuthorize() != null ? project.getFeeAuthorize() : BigDecimal.ZERO;
            BigDecimal payoutFee = project.getPayoutFee() != null ? project.getPayoutFee() : BigDecimal.ZERO;

            // 递归向上汇总
            while (currentCode != null && !currentCode.isEmpty()) {
                String key = currentCode + "-" + authorizeDate + "-" + executeYear;
                ProjectTypeExecuteVo aggregatedProject = aggregatedMap.get(key);
                if (aggregatedProject == null) {
                    aggregatedProject = new ProjectTypeExecuteVo();
                    aggregatedProject.setProjectTypeCode(currentCode);
                    aggregatedProject.setProjectTypeCodeName(codeToName.get(currentCode));
                    aggregatedProject.setUpCode(codeToParent.get(currentCode));
                    aggregatedProject.setAuthorizeDate(authorizeDate);
                    aggregatedProject.setExecuteYear(executeYear);
                    aggregatedProject.setIncomeFee(BigDecimal.ZERO);
                    aggregatedProject.setFeeAuthorize(BigDecimal.ZERO);
                    aggregatedProject.setPayoutFee(BigDecimal.ZERO);
                    aggregatedProject.setIncomeRate(divNullable(aggregatedProject.getIncomeFee(),aggregatedProject.getFeeAuthorize()).multiply(BigDecimal.valueOf(100)));
                    aggregatedProject.setExecuteRate(divNullable(aggregatedProject.getPayoutFee(),aggregatedProject.getFeeAuthorize()).multiply(BigDecimal.valueOf(100)));
                    aggregatedMap.put(key, aggregatedProject);
                }

                // 累加值
                aggregatedProject.setIncomeFee(aggregatedProject.getIncomeFee().add(incomeFee));
                aggregatedProject.setFeeAuthorize(aggregatedProject.getFeeAuthorize().add(feeAuthorize));
                aggregatedProject.setPayoutFee(aggregatedProject.getPayoutFee().add(payoutFee));
                aggregatedProject.setIncomeRate(divNullable(aggregatedProject.getIncomeFee(),aggregatedProject.getFeeAuthorize()).multiply(BigDecimal.valueOf(100)));
                aggregatedProject.setExecuteRate(divNullable(aggregatedProject.getPayoutFee(),aggregatedProject.getFeeAuthorize()).multiply(BigDecimal.valueOf(100)));

                // 移动到父节点
                currentCode = codeToParent.get(currentCode);
            }
        }

        // 将 Map 转换为 List
        return new ArrayList<>(aggregatedMap.values());
    }


    public List<ProjectTypeExecuteVo> buildTree(String code,String date, String year, List<ProjectTypeExecuteVo> treeList) {
        List<ProjectTypeExecuteVo> list = treeList.stream()
                // 筛选父节点
                .filter(t ->
                    ((code == null || code.isEmpty()) && (t.getUpCode() == null || t.getUpCode().isEmpty())) ||
                            (code != null &&  Objects.equals(code,t.getUpCode())  &&  Objects.equals(date,t.getAuthorizeDate()) &&  Objects.equals(year,t.getExecuteYear()))
                )
                // 递归设置子节点
                .map(item -> {
                    item.setChildren(buildTree(item.getProjectTypeCode(),item.getAuthorizeDate(),item.getExecuteYear(), treeList));
                    return item;
                })
                .collect(Collectors.toList());
        return list;
    }

    private BigDecimal divNullable(BigDecimal a, BigDecimal b) {

        if(a.compareTo(BigDecimal.ZERO) == 0){
            return  BigDecimal.ZERO;
        }
        return a.divide(b,4, RoundingMode.HALF_UP);
    }


    /**
     * 学院经费执行率
     * @param executeBo
     * @return
     */
    @Override
    public IPage<ProjectUnitExecuteVo> selectUnitList(PageDomain pageDomain, ProjectTypeExecuteBo executeBo) {
        Page<ProjectUnitExecuteVo> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
        QueryWrapper<ProjectTypeExecuteBo> wrapper = Wrappers.query();
        wrapper.like(StringUtils.isNotBlank(executeBo.getProjectTypeCode()), "p.project_type_code", executeBo.getProjectTypeCode())
                .eq(StringUtils.isNotBlank(executeBo.getAuthorizeDate()), "YEAR (p.authorize_date)", executeBo.getAuthorizeDate())
                .eq(StringUtils.isNotBlank(executeBo.getUnitId()), "p.unit_id", executeBo.getUnitId());
        wrapper.groupBy("p.PROJECT_TYPE_CODE ,p.UNIT_ID,bu.NAME,YEAR (p.AUTHORIZE_DATE),YEAR (COALESCE(p.ACTUAL_END_DATE,p.PLAN_END_DATE))-YEAR(p.BEGIN_DATE) ");
        String orderByColumn = SqlUtil.escapeOrderBySql(pageDomain.getOrderByColumn());
        boolean isASC = SqlUtil.escapeOrderBySql(pageDomain.getIsAsc()).equalsIgnoreCase("asc");
        if (!StringUtils.isEmpty(orderByColumn))
        {
            wrapper.orderBy(true,isASC,orderByColumn);
        }
        return baseMapper.selectUnitList(page,wrapper);
    }

    /**
     * 学院导出
     * @param executeBo
     * @return
     */
    @Override
    public List<ProjectUnitExecuteVo> selectExportUnitList(PageDomain pageDomain, ProjectTypeExecuteBo executeBo) {
        QueryWrapper<ProjectTypeExecuteBo> wrapper = Wrappers.query();
        wrapper.like(StringUtils.isNotBlank(executeBo.getProjectTypeCode()), "p.project_type_code", executeBo.getProjectTypeCode())
                .eq(StringUtils.isNotBlank(executeBo.getAuthorizeDate()), "YEAR (p.authorize_date)", executeBo.getAuthorizeDate())
                .eq(StringUtils.isNotBlank(executeBo.getUnitId()), "p.unit_id", executeBo.getUnitId());
        wrapper.groupBy("p.PROJECT_TYPE_CODE ,p.UNIT_ID,bu.NAME,YEAR (p.AUTHORIZE_DATE),YEAR (COALESCE(p.ACTUAL_END_DATE,p.PLAN_END_DATE))-YEAR(p.BEGIN_DATE) ");
        String orderByColumn = SqlUtil.escapeOrderBySql(pageDomain.getOrderByColumn());
        boolean isASC = SqlUtil.escapeOrderBySql(pageDomain.getIsAsc()).equalsIgnoreCase("asc");
        if (!StringUtils.isEmpty(orderByColumn))
        {
            wrapper.orderBy(true,isASC,orderByColumn);
        }
        return baseMapper.selectUnitList(wrapper);
    }

    /**
     * 项目经费执行率
     * @param executeBo
     * @return
     */
    @Override
    public IPage<ProjectExecuteVo> selectprojectList(PageDomain pageDomain, ProjectTypeExecuteBo executeBo) {
        Page<ProjectExecuteVo> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
        QueryWrapper<ProjectTypeExecuteBo> wrapper = getProjectQuery(executeBo);
        String orderByColumn = SqlUtil.escapeOrderBySql(pageDomain.getOrderByColumn());
        boolean isASC = SqlUtil.escapeOrderBySql(pageDomain.getIsAsc()).equalsIgnoreCase("asc");
        if (!StringUtils.isEmpty(orderByColumn))
        {
            wrapper.orderBy(true,isASC,orderByColumn);
        }
        return baseMapper.selectprojectList(page,wrapper);
    }


    /**
     * 项目导出
     * @param executeBo
     * @return
     */
    @Override
    public List<ProjectExecuteVo> selectExportProList(PageDomain pageDomain,ProjectTypeExecuteBo executeBo) {
        QueryWrapper<ProjectTypeExecuteBo> wrapper = getProjectQuery(executeBo);
        String orderByColumn = SqlUtil.escapeOrderBySql(pageDomain.getOrderByColumn());
        boolean isASC = SqlUtil.escapeOrderBySql(pageDomain.getIsAsc()).equalsIgnoreCase("asc");
        if (!StringUtils.isEmpty(orderByColumn))
        {
            wrapper.orderBy(true,isASC,orderByColumn);
        }
        return baseMapper.selectprojectList(wrapper);
    }

    /**
     * 项目-拼接查询条件
     * @param executeBo
     * @return
     */
    private QueryWrapper<ProjectTypeExecuteBo> getProjectQuery(ProjectTypeExecuteBo executeBo) {
        QueryWrapper<ProjectTypeExecuteBo> wrapper = Wrappers.query();
        Map<String, Object> params = executeBo.getParams();
        wrapper.like(StringUtils.isNotBlank(executeBo.getProjectTypeCode()), "p.project_type_code", executeBo.getProjectTypeCode())
                .eq(StringUtils.isNotBlank(executeBo.getAuthorizeDate()), "YEAR (p.authorize_date)", executeBo.getAuthorizeDate())
                .eq(StringUtils.isNotBlank(executeBo.getUnitId()), "p.unit_id", executeBo.getUnitId())
                .like(StringUtils.isNotBlank(executeBo.getProjectName()), "p.name", executeBo.getProjectName())
                .like(StringUtils.isNotBlank(executeBo.getChargerName()), "p.charger_name", executeBo.getChargerName())
                .between(ObjectUtils.isNotEmpty(params.get("maxfee")) && ObjectUtils.isNotEmpty(params.get("minfee")),
                        "p.fee_authorize", SearchUtils.processAmount(params.get("minfee")), SearchUtils.processAmount(params.get("maxfee")))
                .ge(ObjectUtils.isNotEmpty(params.get("minfee"))  && ObjectUtils.isEmpty(params.get("maxfee")),"p.fee_authorize", SearchUtils.processAmount(params.get("minfee")))
                .le(ObjectUtils.isNotEmpty(params.get("maxfee")) && ObjectUtils.isEmpty(params.get("minfee")),"p.fee_authorize", SearchUtils.processAmount(params.get("maxfee")));
        return wrapper;
    }


    /**
     * 预算科目执行率
     * @param executeBo
     * @return
     */
    @Override
    public List<ProjectBudgetExecuteVo> selectbudgetList(ProjectTypeExecuteBo executeBo) {
        QueryWrapper<ProjectTypeExecuteBo> wrapper = Wrappers.query();
        wrapper.like(StringUtils.isNotBlank(executeBo.getProjectTypeCode()), "bzp.project_type_code", executeBo.getProjectTypeCode())
                .eq(StringUtils.isNotBlank(executeBo.getProjectId()), "b.PROJECT_ID", executeBo.getProjectId())
                .eq( "b.budget_type", "liuxiao")
                .groupBy("s.name,s.ID,s.CODE ,s.UP_CODE ").orderByAsc("s.CODE ");
        return buildTree(null,baseMapper.selectbudgetList(wrapper));
    }

    /**
     * 递归构造
     * @param code
     * @param treeList
     * @return
     */
    public List<ProjectBudgetExecuteVo> buildTree(String code, List<ProjectBudgetExecuteVo> treeList) {
        List<ProjectBudgetExecuteVo> list = treeList.stream()
                // 筛选父节点
                .filter(t -> (StringUtils.isEmpty(code) && StringUtils.isEmpty(t.getUpCode())) || (StringUtils.isNotEmpty(code)&& code.equals(t.getUpCode())))
                // 递归设置子节点
                .map(item -> {
                    item.setChildren(buildTree(item.getSubjectCode(), treeList));
                    return item;
                })
                .collect(Collectors.toList());
        return list;
    }

}
